#include"acceptor.h"

#include<assert.h>
#include<cstring>
#include<sys/socket.h>
#include<sys/types.h>
#include<bits/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include<functional>

#include"address.h"
#include"channel.h"

using namespace tiny_muduo;

Acceptor::Acceptor(EventLoop* loop,const Address& address)
    :loop_(loop),
     listenfd_(::socket(PF_INET,SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC,IPPROTO_TCP)),
     channel_(new Channel(loop,listenfd_)){
        int option_val=1; //用来控制keep-alive功能的开断
        ::setsockopt(listenfd_,SOL_SOCKET,SO_KEEPALIVE,
                    &option_val,static_cast<socklen_t>(sizeof option_val));
        //上面这一整行代码的功能主要就是打开keep-alive
        BindListenFd(address);
        channel_->SetReadCallback(std::bind(&Acceptor::NewConnection,this));
     }

Acceptor::~Acceptor(){
    channel_->DisableAll();
    loop_->Remove(channel_.get());
    ::close(listenfd_);
}

void Acceptor::SetNonBlocking(int fd){
    int old_state=fcntl(fd,F_GETFL);
    int new_state=old_state|O_NONBLOCK;
    fcntl(fd,F_SETFL,new_state);
    (void)new_state;
}

void Acceptor::BindListenFd(const Address& addr){
    struct sockaddr_in address;
    memset(&address,0,sizeof(address));
    address.sin_family=AF_INET;
    address.sin_port=htons(addr.port());
    inet_pton(AF_INET,addr.ip(),&address.sin_addr);
    int ret=bind(listenfd_,(struct sockaddr*)(&address),sizeof(address));
    assert(ret!=-1);
}

void Acceptor::Listen(){
    int ret=listen(listenfd_,SOMAXCONN);
    assert(ret!=-1);
    channel_->EnableReading();
}

void Acceptor::NewConnection(){
    struct sockaddr_in client;
    socklen_t client_addrlength=sizeof(client);
    int connfd=::accept4(listenfd_,(struct sockaddr*)&client,&client_addrlength,SOCK_NONBLOCK|SOCK_CLOEXEC);
//accept4相比accept的优点就是多了一个参数flags，可以设置SOCK_NONBLOCK、SOCK_CLOEXEC等参数，最大的好处就是可以直接把socket设置为非阻塞，不然还要自己写函数实现socket的非阻塞
    int option_val=1;
    ::setsockopt(listenfd_,SOL_SOCKET,SO_KEEPALIVE,
                &option_val,static_cast<socklen_t>(sizeof option_val));
    assert(connfd>0);
    new_connection_callback_(connfd);
}